package a日常研究._03java线程._03线程交替打印;

import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用多线程交替打印1~100（A、B、C）
 */
public class _01ThreeThreadsCrossPrintTest {
    public static final int MAX_NUM = 100;
    public static void main(String[] args) {
        // 01，普通对象锁，需要互相竞争
//        new Thread(new PrintRunnableUseObjectLock(0), "A").start();
//        new Thread(new PrintRunnableUseObjectLock(1), "B").start();
//        new Thread(new PrintRunnableUseObjectLock(2), "C").start();
        //02，使用JUC锁进行优化，实现精准唤醒
//        Condition aLock = LOCK.newCondition();
//        Condition bLock = LOCK.newCondition();
//        Condition cLock = LOCK.newCondition();
//        new Thread(new PrintRunnableUseJUCLock(0, aLock, bLock), "A").start();
//        new Thread(new PrintRunnableUseJUCLock(1, bLock, cLock), "B").start();
//        new Thread(new PrintRunnableUseJUCLock(2, cLock, aLock), "C").start();
        //03，使用Semaphore
//        new Thread(new PrintRunnableUseSemaphore(a1, b2), "A").start();
//        new Thread(new PrintRunnableUseSemaphore(b2, c3), "B").start();
//        new Thread(new PrintRunnableUseSemaphore(c3, a1), "C").start();
        //04，三个线程循环打印ACB，其中A打印两次，B打印三次，C打印四次
        Condition aLock = LOCK.newCondition();
        Condition bLock = LOCK.newCondition();
        Condition cLock = LOCK.newCondition();
        new Thread(new AdvancedPrintRunnableUseJUCLock(0, aLock, bLock), "A").start();
        new Thread(new AdvancedPrintRunnableUseJUCLock(1, bLock, cLock), "B").start();
        new Thread(new AdvancedPrintRunnableUseJUCLock(2, cLock, aLock), "C").start();
    }

    /**
     * 使用普通对象锁控制
     */
    private static class PrintRunnableUseObjectLock implements Runnable {
        public static int curNum = 0;
        private final int target;

        public PrintRunnableUseObjectLock(int target) {
            this.target = target;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    synchronized (PrintRunnableUseObjectLock.class) {
                        while (curNum % 3 != target) {
                            PrintRunnableUseObjectLock.class.wait();
                        }
                        if (curNum >= MAX_NUM) {
                            break;
                        }
                        System.out.println(Thread.currentThread().getName() + "->print: " + ++curNum);
                        Thread.sleep(500);
                        PrintRunnableUseObjectLock.class.notifyAll();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 优化1，使用JUC锁实现精准唤醒
     */
    public static final Lock LOCK = new ReentrantLock();
    private static class PrintRunnableUseJUCLock implements Runnable {
        public static int curNum = 0;
        private final int target;
        private final Condition curThread;
        private final Condition nextThread;

        public PrintRunnableUseJUCLock(int target, Condition curThread, Condition nextThread) {
            this.target = target;
            this.curThread = curThread;
            this.nextThread = nextThread;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    LOCK.lock();
                    while (curNum % 3 != target) {
                        curThread.await();
                    }
                    if (curNum >= MAX_NUM) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "->print: " + ++curNum);
                    Thread.sleep(500);
                    nextThread.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    LOCK.unlock();
                }
            }
        }
    }

    /**
     * 优化2，使用Semaphore实现精准唤醒
     */
    public static Semaphore a1 = new Semaphore(1);  //先启动A，这个设置为1
    public static Semaphore b2 = new Semaphore(0);
    public static Semaphore c3 = new Semaphore(0);
    private static class PrintRunnableUseSemaphore implements Runnable {
        public static int curNum = 0;
        private final Semaphore curThread;
        private final Semaphore nextThread;

        public PrintRunnableUseSemaphore(Semaphore curThread, Semaphore nextThread) {
            this.curThread = curThread;
            this.nextThread = nextThread;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    // 这里可以不判断了
                    curThread.acquire();
                    if (curNum >= MAX_NUM) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "->print: " + ++curNum);
                    Thread.sleep(500);
                    nextThread.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 进阶，三个线程循环打印ACB，其中A打印两次，B打印三次，C打印四次
     */
    private static class AdvancedPrintRunnableUseJUCLock implements Runnable {
        public static int curNum = 0;
        private final int target;
        private final Condition curThread;
        private final Condition nextThread;

        public AdvancedPrintRunnableUseJUCLock(int target, Condition curThread, Condition nextThread) {
            this.target = target;
            this.curThread = curThread;
            this.nextThread = nextThread;
        }

        public String getPrintChar() {
            int idx;
            if (curNum % 9 < 2) {
                idx = 0;
            } else if (curNum % 9 < 5) {
                idx = 1;
            } else {
                idx = 2;
            }
            return String.valueOf((char) ('A' + idx));
        }

        @Override
        public void run() {
            while (true) {
                try {
                    LOCK.lock();
                    while (curNum % 3 != target) {
                        curThread.await();
                    }
                    if (curNum >= MAX_NUM) {
                        break;
                    }
                    System.out.println(Thread.currentThread().getName() + "->print: " + getPrintChar());
                    curNum++;
                    Thread.sleep(500);
                    nextThread.signal();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    LOCK.unlock();
                }
            }
        }
    }
}
